#============================================
# #知识点
# tcp列子
#服务端
import socket
import subprocess
obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.bind(("127.0.0.1",8788))
obj.listen(5)
conn,ip_port = obj.accept()
print(ip_port)
while True:
   try:
        data = conn.recv(1024)
        if len(data) == 0:    
            break
        obj=subprocess.Popen(data.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        print(len(stderr)+len(stdout))
        conn.send(stdout+stderr)
   except:
       break
conn.close()


#客户端
import socket
import socket
obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.connect(("127.0.0.1",8788))
while True:
    msg = input("输入一个windons命令:")
    if len(msg) == 0:
        continue
    obj.send(msg.encode("utf-8"))
    data = obj.recv(1024)         
    print(data.decode("gbk"))      #windons是gbk形式
obj.close()
#============================================
#上面的列子是运用subprocess来远程实现windons上面的命令
#根据上面的代码，引发一个问题，那就是粘包问题，
#因为无论是从发送还是接受，都是从自己的内存中去获取数据
#如果设置数据的获取的内存小于要收到内存，那么有一部分的数据就会停留在服务端/客户端的缓存中(因为tcp通信为流水形式)
#所以再进行接受数据就会漏掉剩余的数据,这样下一次再执行代码时，就会返回上一次没有发完的数据,导致数据混乱（粘包）
#这里提高接受的数据的容量来解决粘包是没有用，要考虑到有可能发送慢了，或者你设置的容量超过缓存容量，这样就是无效的.
#============================================
#粘包问题出现的原因
# 1、tcp是流式协议，数据像水流一 样粘在一 起，没有任何边界区分
# 2、收数据没收干净，有残留，就会下一次结果混淆在一起
# 解决的核心法门就是:每次都收干净不要任何残貿
#这里提供一种思路就是将要发送的数据的长度返回给客户端，让客户端判断收到的数据是否达到需要的长度如果没达到就一直接受，
# 达到了，就结束接受
#============================================
# TCP（transport control protocol，传输控制协议）是面向连接的，面向流的，提供高可靠性服务。
# 收发两端（客户端和服务器端）都要有一一成对的socket，因此，发送端为了将多个发往接收端的包，
# 更有效的发到对方，使用了优化方法（Nagle算法），将多次间隔较小且数据量小的数据，合并成一个大的数据块，
# 然后进行封包。这样，接收端，就难于分辨出来了，必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。

# UDP（user datagram protocol，用户数据报协议）是无连接的，面向消息的，提供高效率服务。
# 不会使用块的合并优化算法，, 由于UDP支持的是一对多的模式，所以接收端的skbuff(套接字缓冲区）
# 采用了链式结构来记录每一个到达的UDP包，在每个UDP包中就有了消息头（消息来源地址，端口等信息），
# 这样，对于接收端来说，就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
#============================================
#解决粘包问题思路:
# 1、拿到数据的总大小total_ size
# 2、recv_ size=0,循环接收，每接收-次，recv_ size+=接收的长度
#了、直到recv_ size=total_ size, 结束循环
# 3、这里拿到数据总大小是一个困难，可以用strurt来解决,用strut.pack("i",数据)把数据变成4位的字符当作头发送出去只支持(utf-8)
#可以先接受4个数据，那头文件拿出来用unpack解包吧总数据大小拿出来即可
#方法1
#服务端
import socket
import subprocess
import struct
obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.bind(("127.0.0.1",8788))
obj.listen(5)
conn,ip_port = obj.accept()
print(ip_port)
while True:
    try:
        data = conn.recv(1024)
        if len(data) == 0:    
            break
        obj=subprocess.Popen(data.decode("utf-8"),
                             shell=True,stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        print(len(stderr)+len(stdout))
        zong_data = len(stderr)+len(stdout)
        zong_data = struct.pack("i",zong_data)
        conn.send(zong_data)
        conn.send(stdout)
        conn.send(stderr)
    except:
        break
conn.close()



#客户端
import socket
import struct
obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.connect(("127.0.0.1",8788))
while True:
    msg = input("输入一个windons命令:")
    if len(msg) == 0:
        continue
    obj.send(msg.encode("utf-8"))
    zong_data = obj.recv(4)
    zong_data = struct.unpack("i",zong_data)[0]
    rev_data = 0
    data = b""
    while rev_data < zong_data:
        fdata = obj.recv(1024)
        rev_data+= len(fdata)
        data += fdata
    print(data.decode("gbk"))
obj.close()
#============================================
#方法2（方法1升级版）
#服务端
import socket
import subprocess
import struct
import json
obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.bind(("127.0.0.1",8788))
obj.listen(5)
conn,ip_port = obj.accept()
print(ip_port)
while True:
    try:
        data = conn.recv(1024)
        if len(data) == 0:    
            break
        obj=subprocess.Popen(data.decode("utf-8"),
                             shell=True,stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        print(len(stderr)+len(stdout))
        zong_data = len(stderr)+len(stdout)
        heard_dic = {
            "lens":zong_data,
            "name":"套用方法"
            }
        json_len = json.dumps(heard_dic)
        print(json_len)
        json_len_x = json_len.encode("utf-8")
        zong_datae = struct.pack("i",len(json_len_x))
        conn.send(zong_datae)
        conn.send(json_len_x)
        conn.send(stdout)
        conn.send(stderr)
    except:
        break
conn.close()


#客户端
import socket
import struct
import json
obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.connect(("127.0.0.1",8788))
while True:
    msg = input("输入一个windons命令:")
    if len(msg) == 0:
        continue
    obj.send(msg.encode("utf-8"))
    x= obj.recv(4)
    zong_datae = struct.unpack("i",x)[0]
    zong_data = obj.recv(zong_datae)
    heard_len = zong_data.decode("utf-8")
    heard = json.loads(heard_len)
    zongdata = heard["lens"]
    rev_data = 0
    data = b""
    while rev_data < zongdata:
        fdata = obj.recv(1024)
        rev_data+= len(fdata)
        data += fdata
    print(data.decode("gbk"))
obj.close()
#============================================


